package com.gframework.core.service.spring;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BinaryOperator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;

import com.gframework.core.service.PriorityOrderUtil;
import com.gframework.util.GUtils;

/**
 * <strong>【数据字典唯一操作类】</strong>.
 * 本类同时也是一个spring的bean。
 * <p>
 * 你可以通过创建 {@link ExtendDataDictionary} 接口的bean来初始化数据字典信息，数据字典和配置信息不一样，
 * 他不是那么的着急初始化，因此他的初始化会靠后执行，所有请不要再其他bean初始化过程中就去
 * 获取字典信息。
 * </p>
 * <p>
 * 数据字典是按照类型进行存储的，一个类型下会有多个{@code code-value} 对应关系。
 * </p>
 * 
 * @since 1.0.0
 * @author Ghwolf
 * 
 * @see ExtendDataDictionary
 * @see DataDictionarySource
 * @see DynamicSource
 *
 */
public class DataDictionary implements InitializingBean {
	private static final Logger logger = LoggerFactory.getLogger(DataDictionary.class);
	/**
	 * 在spring初始化的时候，将本类对象赋值给静态变量，以便于可以取得配置内容.<br>
	 * 只会实例化一次。
	 */
	private static DataDictionary instance ;
	/**
	 * 扩展数据字典加载对象
	 */
	private ExtendDataDictionary[] extendDataDictionary ;
	
	/**
	 * 字典信息查询map.
	 * 这个对象不是线程安全的，采用reinitOnWrite方式进行线程安全控制
	 */
	private Map<String,MultipartDataDictionarySource> typeSourceMap ;
	
	public DataDictionary(ExtendDataDictionary[] extendDataDictionary) {
		PriorityOrderUtil.sort(extendDataDictionary);
		this.extendDataDictionary = extendDataDictionary;
		logger.info("====> 已启用 DataDictionary 数据字典信息获取类！所有数据字典信息将由本类进行获取！");
	}
	
	/**
	 * 初始化或重新初始化
	 */
	private synchronized void reload() {
		Map<String,List<DataDictionarySource>> map = new HashMap<>();
		for (ExtendDataDictionary data : this.extendDataDictionary) {
			for (DataDictionarySource source : data.loadDataDictionary()) {
				List<DataDictionarySource> list = map.get(source.getType());
				if (list == null) {
					list = new ArrayList<>();
					map.put(source.getType(),list);
				}
				list.add(source);
			}
		}
		Map<String,MultipartDataDictionarySource> sourceMap = new HashMap<>();
		for (Map.Entry<String,List<DataDictionarySource>> entry : map.entrySet()) {
			MultipartDataDictionarySource source = new MultipartDataDictionarySource(entry.getKey(), entry.getValue());
			sourceMap.put(entry.getKey(), source);
		}
		this.typeSourceMap = sourceMap;
	}
	
	@Override
	public void afterPropertiesSet() throws Exception {
		if (instance != null) {
			throw new BeanInitializationException("DataDictionary应当是单例的，但是却被创建了两次");
		}
		instance = this ;
		reload();
	}
	
	/**
	 * 获取某个类型的指定code的数据字典信息.
	 * @param type 数据字典类型
	 * @param code 要查询的code
	 * @return 返回数据字典信息，没有返回null
	 */
	private String getData0(String type,String code) {
		MultipartDataDictionarySource source = this.typeSourceMap.get(type);
		return source == null ? null : source.getData(code);
	}
	/**
	 * 获取某个类型的全部code值.
	 * @param type 数据字典类型
	 * @return 将该类型所有的code值以Collection集合的形式返回。
	 */
	private Collection<String> getCodes0(String type) {
		MultipartDataDictionarySource source = this.typeSourceMap.get(type);
		return source == null ? Collections.emptyList() : source.getCodes();
	}
	/**
	 * 获取某个类型的全部value值.
	 * @param type 数据字典类型
	 * @return 将该类型所有的value值以Collection集合的形式返回。
	 */
	private Collection<String> getValues0(String type) {
		MultipartDataDictionarySource source = this.typeSourceMap.get(type);
		return source == null ? Collections.emptyList() : source.getValues();
	}
	
	/**
	 * 获取某个类型的指定code的数据字典信息.
	 * @param type 数据字典类型
	 * @param code 要查询的code
	 * @return 返回数据字典信息，没有返回null
	 */
	public static String getData(String type,String code) {
		return instance.getData0(type, code);
	}

	/**
	 * 获取某个类型的指定code的数据字典信息.
	 * @param type 数据字典类型
	 * @param code 要查询的code
	 * @param defaultValue 默认值，如果没有获取到数据，则返回默认值
	 * @return 返回数据字典信息，没有返回默认值
	 */
	public static String getData(String type,String code,String defaultValue) {
		return GUtils.nvl(instance.getData0(type, code), defaultValue);
	}
	
	/**
	 * 获取某个类型的指定code的数据字典信息.
	 * @param type 数据字典类型
	 * @param code 要查询的code
	 * @param getDefaultValue 获取默认值的方法，不能为null。第一个参数为type，第二个为code，需要返回value
	 * @return 返回数据字典信息，没有返回默认值
	 */
	public static String getData(String type,String code,BinaryOperator<String> getDefaultValue) {
		String value = instance.getData0(type, code);
		return value == null ? getDefaultValue.apply(type, code) : value ;
	}
	/**
	 * 获取某个类型的全部code值.
	 * @param type 数据字典类型
	 * @return 将该类型所有的code值以Collection集合的形式返回。
	 */
	public static Collection<String> getCodes(String type) {
		return instance.getCodes0(type);
	}
	/**
	 * 获取某个类型的全部value值.
	 * @param type 数据字典类型
	 * @return 将该类型所有的value值以Collection集合的形式返回。
	 */
	public static Collection<String> getValues(String type) {
		return instance.getValues0(type);
	}

}
